#include <usr/enviroment.h>
#include <usr/libsodium.h>

/********************************************************
* Biblioteca de enviroment de procesos usuario.
* Su funcionalid es:
    - Proveer funciones de consulta, edicion y herencia de enviroment de usuario
* Autor: Sergio Martin 2010
*********************************************************/

/**
\fn int iObtenerArgumentos(char* stCadenaArgs, char*** pstArgv)
\brief Genera un puntero doble compatible con la forma POSIX de pasar argv y enviroment para execve. Sergio Martin 2010.
\param stCadenaArgs es la cadena de argumentos separados por espacios
\param pstArgv es el puntero al puntero doble.
\return iArgc devuelve la cantidad de argumentos incluyendo el primero.
\date 08/04/2010
*/
char** pstFnObtenerArgumentos(char* stCadenaArgs)
{
  int iArgc;
  int iContPuntero;
  int i,j;
  char*  stTemp;
  char** pstArgv;
  


  // Obtenemos ARGC
    i = 0;
    iArgc = 1; // Siempre va a estar el nombre del ejecutable
    while(stCadenaArgs[i] == ' ') i++;
    while(stCadenaArgs[i] != '\0')
     {
     	if(stCadenaArgs[i] == ' ')
     	 {
     	   while(stCadenaArgs[i] == ' ') i++;
     	   if(stCadenaArgs[i] != '\0')iArgc++;
     	   i--;
     	 }
        i++;
     }
     

	 
  // De ahora en mas usamos stTemp para no alterar la cadena recibida
   stTemp = (char*)calloc((i+2)*sizeof(char));
   if(stTemp < 0) return -1;
   for(j = 0; j < i; j++) stTemp[j] = stCadenaArgs[j];
    stTemp[i] = '\0';
	
	
  // Creamos el vector de punteros ARGV
  pstArgv = (char**)calloc((iArgc+1)*sizeof(char*)); // El +1 es por el vector nulo
  if(pstArgv < 0) return -1;
  
  i = 0;
  iContPuntero = 0;
  while(stTemp[i] == ' ') i++;
  while(stTemp[i] != '\0')
   {
     if(i == 0) pstArgv[iContPuntero++] = stTemp;
     else if (stTemp[i] != ' ' && stTemp[i-1] == ' '){pstArgv[iContPuntero++] = stTemp+i; }
     i++;
   }
  
  // Clausuramos los argumentos (eliminando espacios)
   i = 0;
   while(stTemp[i] != '\0'){ if(stTemp[i] == ' ') stTemp[i] = '\0'; i++;}
   pstArgv[iArgc] = 0; // Vector nulo.


   //for(i=0; i < iArgc; i++) {iFnImprimir("\n"); iFnImprimir("%s",pstArgv[i]);}

  return pstArgv;
}


/**
 * @brief Inicializa el enviroment de usuario en el heap (lectura/escritura) a partir del heredado en el stack (solo lectura)
 * @param stPatron. Es el patron a buscar del lado derecho del '='
 * @param stAsignacion. Es una cadena conteniendo la asignacion con el formato "VARIABLE=VALOR_ALFANUMERICO"
 * @return 0 Si se corresponden, -1 si no.
 * @date 15/03/2010
 */
int iFnInicializarEnviromentUsuario(char** pstEnviromentStack)
{
    int i,j,k;
	int iSizeOfEnviroment;
	
	// Cuento la cantidad de asignaciones del Enviroment
	iCantAsignaciones = 0;
	while(pstEnviromentStack[iCantAsignaciones] != 0) iCantAsignaciones++;
	
	
	// Le doy la memoria necesaria al vector de punteros para resguardar el enviroment de stack
	pstEnviroment = (char**) calloc(sizeof(char*)*iCantAsignaciones);
	
	// Cuento la cantidad de bytes que ocupa cada asignacion, reservo la memoria necesaria para cada una y copio el contenido del stack al heap
	for( i = 0; i < iCantAsignaciones; i++)
	  {
	    j = 0;
		while(pstEnviromentStack[i][j] != '\0') j++;
		pstEnviroment[i] = (char*) calloc(sizeof(char)*(j+1)); // El +1 es el byte que nos falta contar por el '\0'
		for(k = 0; k < j; k++) pstEnviroment[i][k] = pstEnviromentStack[i][k]; // Copio el contenido de cada asignacion del stack al heap
		
	  }
	  
	  
	// Seteamos iEnviromentInicializado en 1 para habilitar el uso de las funciones de administracion
	iEnviromentInicializado = 1;
}


/**
 * @brief Obtiene el valor numerico derecho de una asignacion.
 * @param stAsignacion. Es una cadena conteniendo la asignacion con el formato "VARIABLE=VALOR_ALFANUMERICO"
 * @date 15/03/2010
 */
int iFnEnvObtenerValorNumerico(char *stAsignacion)
{
  int i;
  i = 0;
  
  
  while(stAsignacion[i] != '\0')
    {
	   if(stAsignacion[i] == '=') return iFnCtoi(stAsignacion+i+1);
	   i++;
	}
	return -1;
}

/**
 * @brief Obtiene el valor numerico derecho a partir del nombre de una variable.
 * @param stPatron. Patron izquierdo de una asignacion PATRON=######
 * @date 15/03/2010
 */
int iFnEnvObtenerValorNumericoDeVariable(char *stPatron)
{
  int i,j;
  
  
  for(i = 0; i < iCantAsignaciones; i++)
    {
      if(iFnEnvCompararVariable(stPatron,pstEnviroment[i]) == 0)
	   {
	      j = 0;
		  while(pstEnviroment[i][j] != '\0')
			{
			   if(pstEnviroment[i][j] == '=') return iFnCtoi(pstEnviroment[i] + j +1);
			   j++;
			}
			return -1;
		}
	}
    return -1;
}

/**
 * @brief Indica si el patron pasado corresponde al nombre izquierdo de una asignacion.
 * @param stPatron. Es el patron a buscar del lado izquierdo del '='
 * @param stAsignacion. Es una cadena conteniendo la asignacion con el formato "VARIABLE=VALOR_NUMERICO"
 * @return 0 Si se corresponden, -1 si no.
 * @date 15/03/2010
 */
int iFnEnvCompararVariable(char *stPatron, char *stAsignacion)
{
  int i;
  i = 0;  
  
  while(stAsignacion[i] != '\0' && stPatron != '\0' && stAsignacion[i] == stPatron[i])  i++; 
  if(stPatron[i] == '\0' && stAsignacion[i] == '=') return 0;
  return -1;
}

/**
 * @brief Indica si el valor pasado corresponde al valor derecho de una asignacion.
 * @param stPatron. Es el patron a buscar del lado derecho del '='
 * @param stAsignacion. Es una cadena conteniendo la asignacion con el formato "VARIABLE=VALOR_ALFANUMERICO"
 * @return 0 Si se corresponden, -1 si no.
 * @date 15/03/2010
 */
int iFnEnvCompararValor(char *stPatron, char *stAsignacion)
{
    
	
  while(*stAsignacion != '=' && *stAsignacion != '\0') stAsignacion++;
  if(*stAsignacion == '\0') return -1;
  
  stAsignacion++;

  while(*stAsignacion != '\0' && *stPatron != '\0' && *stAsignacion == *stPatron){stAsignacion++; stPatron++;}
  if(*stAsignacion == '\0' &&  *stPatron == '\0') return 0;
  return -1;
}

/**
 * @brief Indica si existe el valor pasado para la variable.
 * @param stPatronIzquierdo. Es el nombre de la variable a buscar del lado izquierdo de la Asignacion
 * @param stPatronDerecho. Es el valor a confirmar de la variable en cuestion
 * @return 0 Si se corresponden, -1 si no.
 * @date 15/03/2010
 */
int iFnEnvCompararValorDeVariable(char *stPatronIzquierdo, char *stPatronDerecho)
{
    int i;
	
	
	for(i=0;i<iCantAsignaciones;i++)  if(iFnEnvCompararVariable(stPatronIzquierdo,pstEnviroment[i]) == 0) return iFnEnvCompararValor(stPatronDerecho,pstEnviroment[i]);
	return -1;
}

/**
 * @brief Permite agregar o cambiar una asignacion nueva al Enviroment del heap.
 * @param stAsignacion. Es una cadena conteniendo la asignacion con el formato "VARIABLE=VALOR_ALFANUMERICO"
 * @return 0 Si se agrego/cambio satisfactoriamente. -1 si hubo un error.
 * @date 15/03/2010
 */
int iFnEnvSet(char *stAsignacion)
{
   int i;
   int iTamPatron;
   int iTamAsignacion;
   int iSoloBorrar;
   
   char *stPatron;
   char *stAsignacionTemp;
   
   char **pstEnviromentProvisorio;
   
   if (iEnviromentInicializado == 0) return -1;
   
   // Obtengo el patron de la asignacion (el lado izquierdo del =)
	   i = 0;
	   iSoloBorrar = 0;
	   while(stAsignacion[i] != '=' && stAsignacion[i] != '\0') i++;
	   if(stAsignacion[i] == '\0') return -1; // Significa que no hay un =!
	   if(stAsignacion[i+1] == '\0') iSoloBorrar = 1; // Si tiene la forma VARIABLE= significa que hay que borrar unicamente
	   iTamPatron = i+1; // el +1 para el ultimo caracter '\0'
	   stPatron = (char*)calloc(sizeof(char)*(iTamPatron)); // Reservamos espacio para el patron
	   for(i=0; i < iTamPatron; i++) stPatron[i] = stAsignacion[i]; // Obtenemos el patron
	   stPatron[iTamPatron-1] = '\0'; // Clausuramos el patron sacando el =
   
   // Borro cualquier existencia del mismo patron dentro del enviroment
       iFnEnvUnSet(stPatron);
	   
	   if(iSoloBorrar == 1) return 0;
	   
	   
   // Reservo espacio para la asignacion entera y la copio.
	  while(stAsignacion[i] != '\0') i++;
	  iTamAsignacion = i+1;
	  stAsignacionTemp = (char*)calloc(sizeof(char)*iTamAsignacion);
	  for(i=0; i < iTamAsignacion; i++) stAsignacionTemp[i] = stAsignacion[i]; // Obtenemos la asignacion
	  stAsignacionTemp[i] = '\0'; // Clausuramos la asignacion
	  
   // Creo un enviroment provisorio con una posicion mas a la que le asigno la direccion de la asignacion temporal.
	  iCantAsignaciones++;
	  pstEnviromentProvisorio = (char**)calloc(sizeof(char*)*iCantAsignaciones);
	  for( i = 0; i < (iCantAsignaciones - 1); i++) pstEnviromentProvisorio[i] = pstEnviroment[i];
	  pstEnviromentProvisorio[i] = stAsignacionTemp;
	  
	// Libero el puntero de enviroment viejo y le asigno el nuevo.
	  free(pstEnviroment);
	  pstEnviroment = pstEnviromentProvisorio;
	  
	  return 0;
}

/**
 * @brief Permite eliminar una asignacion del Enviroment del heap.
 * @param stPatron. Es el patron a buscar del lado izquierdo del '='
 * @return 0 Si se elimino satisfactoriamente. -1 si no se encontro el patron.
 * @date 15/03/2010
 */
int iFnEnvUnSet(char *stPatron)
{
   int i;
   int iPosicion;
   char** pstEnviromentProvisorio;
   
   if (iEnviromentInicializado == 0) return -1;
   
   iPosicion = iFnEnvBuscarPosicion(stPatron);
   if(iPosicion == -1) return -1;
   
   // Creamos un Enviroment provosorio con una posicion menos.
   pstEnviromentProvisorio = (char**)calloc((iCantAsignaciones -1)*sizeof(char*));
   
   // Reducimos en 1 la cantidad de asignaciones
   iCantAsignaciones--;
   
   // Desde el elemento borrado, se copian las asignaciones siguientes una posicion hacia abajo
   for(i = iPosicion; i < iCantAsignaciones; i++) pstEnviroment[i] = pstEnviroment[i+1];
   
   // Copiamos el enviroment al provisorio, liberamos la memoria y asignamos al provisorio como el nuevo puntero al enviroment
   for(i = 0; i < iCantAsignaciones; i++) pstEnviromentProvisorio[i] = pstEnviroment[i];
   free(pstEnviroment);
   pstEnviroment = pstEnviromentProvisorio;
   
   return 0;
}

/**
 * @brief Permite determinar la posicion dentro del enviroment de una asignacion dada.
 * @param stPatron. Es el patron a buscar del lado izquierdo del '='
 * @return La posicion dentro del enviroment si se encontro, -1 si no se encontro.
 * @date 15/03/2010
 */
int iFnEnvBuscarPosicion(char *stPatron)
{
    int i;
	
	if (iEnviromentInicializado == 0) return -1;
	
	for(i = 0; i < iCantAsignaciones; i++)  if(iFnEnvCompararVariable(stPatron,pstEnviroment[i]) == 0) return i;
	return -1;
}

/**
 * @brief Lista en consola las asignaciones del enviroment
 * @return 0
 * @date 15/03/2010
 */
int iFnEnvListarEnviroment()
{
    int i;
	
	if (iEnviromentInicializado == 0) return -1;
	
	for(i = 0; i < iCantAsignaciones; i++)  iFnImprimir("\n%s",pstEnviroment[i]);
	return 0;
}

/**
 * @brief Devuelve el puntero al Enviroment con un puntero nulo al final del vector.
 * @return Un puntero doble al enviroment + un puntero nulo al final. Si falla el calloc retorna -1
 * @date 15/03/2010
 */
char** pstFnEnvObtenerEnviroment()
{
  int i;
  char** pstEnviromentConNulo;
  
  if (iEnviromentInicializado == 0) return -1;
  
  pstEnviromentConNulo = (char**)calloc((iCantAsignaciones+1)*sizeof(char*));
  if(pstEnviromentConNulo < 0) return -1;
  
  for(i = 0; i < iCantAsignaciones; i++) pstEnviromentConNulo[i] = pstEnviroment[i];
  pstEnviromentConNulo[i] = 0; // Le pongo el puntero nulo al final
  
  return pstEnviromentConNulo;
}
